package cn.pku.ss.sirx.sna.ib;

import java.util.LinkedList;
import java.util.Queue;

import cn.pku.ss.sirx.sna.ib.bean.Message;
import cn.pku.ss.sirx.sna.ib.bean.ReplyTree;
import cn.pku.ss.sirx.sna.ib.bean.ReplyTreeNode;

/**
 * Build messages in a message board.
 *
 * @author SirX
 * @version 1.0
 */
public class MessageBuilder implements IBuilder<Message> {

    public MessageBuilder(ReplyTreeQueue replyTreeQueue) {
        this.replyTreeQueue = replyTreeQueue;
    }

    @Override
    public void build(Message message) {
        /*
         * Assume that "A" means the message board owner;
         * other characters such as "B" and "C" means the replier.
         */
        String ownerId = this.replyTreeQueue.getMessageBoard().getOwnerId();
        String fromId = message.getFromId();
        String toId = message.getToId();
        // fix some defects in database
        if (toId == null) {
            message.setToId(ownerId);
            toId = ownerId;
        }
        // A -> A
        if (fromId.equals(toId)) return; // note: no such condition like B -> B
        
        // A -> B
        if (fromId.equals(ownerId)) {
            ReplyTree targetReplyTree = this.getTargetReplyTree(toId);
            // target reply tree found
            if (targetReplyTree != null) {
                ReplyTreeNode targetReplyTreeNode = this.getLatestReplyTreeNode(targetReplyTree, toId);
                targetReplyTreeNode.getChildren().add(new ReplyTreeNode(message));
            }
            // target reply tree NOT found: lost type 1
            else {
                this.replyTreeQueue.enqueueLostReply(new ReplyTreeNode(message));
            }
        }
        
        // B -> A
        else if (toId.equals(ownerId)) {
            // is a new reply
            if (message.isNewReply()) {
                this.insertNewReply(message);
            }
            // NOT a new reply
            else {
                ReplyTree targetReplyTree = this.getTargetReplyTree(fromId);
                // target reply tree found
                if (targetReplyTree != null) {
                    ReplyTreeNode ownerReplyTreeNode = this.getLatestReplyTreeNode(targetReplyTree, ownerId);
                    // A found
                    if (ownerReplyTreeNode != null) {
                        ownerReplyTreeNode.getChildren().add(new ReplyTreeNode(message));
                    }
                    // A NOT found, treat as a new reply
                    else {
                        this.insertNewReply(message);
                    }
                }
                // target reply tree NOT found, treat as a new reply
                else {
                    this.insertNewReply(message);
                }
            }
        }
        
        // B -> C
        else {
            ReplyTree targetReplyTree = this.getTargetReplyTree(toId);
            // C reply tree found
            if (targetReplyTree != null) {
                ReplyTreeNode targetReplyTreeNode = this.getLatestReplyTreeNode(targetReplyTree, toId);
                targetReplyTreeNode.getChildren().add(new ReplyTreeNode(message));
            }
            else {
                targetReplyTree = this.getTargetReplyTree(fromId);
                // B reply tree found
                if (targetReplyTree != null) {
                    ReplyTreeNode targetReplyTreeNode = this.getLatestReplyTreeNode(targetReplyTree, toId);
                    // C found
                    if (targetReplyTreeNode != null) {
                        targetReplyTreeNode.getChildren().add(new ReplyTreeNode(message));
                    }
                    // C NOT found: lost type 2
                    else {
                        this.replyTreeQueue.enqueueLostReply(new ReplyTreeNode(message));
                    }
                }
                else {
                    // both B and C reply tree NOT found: lost type 2
                    this.replyTreeQueue.enqueueLostReply(new ReplyTreeNode(message));
                }
            }
        }
    }
    
    private ReplyTree getTargetReplyTree(String replierId) {
        // search from latest to previous
        for (ReplyTree replyTree = this.replyTreeQueue.rear(); replyTree != null; replyTree = replyTree.getPrevious()) {
            if (replyTree.getReplierId().equals(replierId)) {
                return replyTree;
            }
        }
        return null;
    }
    
    private ReplyTreeNode getLatestReplyTreeNode(ReplyTree replyTree, String replierId) {
        Queue<ReplyTreeNode> queue = new LinkedList<ReplyTreeNode>();
        queue.offer(replyTree.getRoot());
        ReplyTreeNode result = null;
        while (!queue.isEmpty()) {
            ReplyTreeNode node = queue.poll();
            if (node.getMessage().getFromId().equals(replierId)) {
                result = node;
            }
            queue.addAll(node.getChildren());
        }
        return result;
    }
    
    private void insertNewReply(Message message) {
        ReplyTree replyTree = new ReplyTree(new ReplyTreeNode(message));
        this.replyTreeQueue.enqueue(replyTree);
    }
    
    //////////////////////////////////////////////////////////////////////////////

    private ReplyTreeQueue replyTreeQueue;

}
